﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.ServiceModel;
using AccessControl.Common;
using AccessControl.Utility.Tools;
using AccessControl.WorkflowEngine.FingerService;
using AccessControl.WorkflowEngine.Properties;
using AccessControl.WorkflowEngine.Struct;


namespace AccessControl.WorkflowEngine
{
    [ServiceBehavior(
   ConcurrencyMode = ConcurrencyMode.Single,
   InstanceContextMode = InstanceContextMode.Single)]

    public class WorkflowEngineService : IWorkflowEngineServer, IDisposable
    {
        #region Fields

        private static readonly Dictionary<Guid, IWorkflowEngineCallback> _callbackDic = new Dictionary<Guid, IWorkflowEngineCallback>();
        private static readonly Dictionary<Int32, WorkflowSubscriber> _workflowSubscriberDic = new Dictionary<int, WorkflowSubscriber>();
        private static readonly Dictionary<Int32, WorkflowEngine> _workflowDic = new Dictionary<int, WorkflowEngine>();
        #endregion

        #region Constructor

        public WorkflowEngineService()
        {
            try
            {
                PrepareWorkflowList();
                StartAllWorkflows();

            }
            catch (Exception ex)
            {
                WriteLog(ex.Message, "WorkflowEngineService Constructor", eventLevelConstants.Error);
            }

        }

        #endregion

        #region Workflow Engine Methods

        private static void PrepareWorkflowList()
        {
            try
            {
                WorkflowEngine.Initialize();

                if (WorkflowEngine.tblWorklow == null) return;

                foreach (var workflow in from AccessControlDS.tblWorkflowRow wf in WorkflowEngine.tblWorklow.Rows select new WorkflowEngine(wf))
                {
                    _workflowDic.Add(workflow.WorkflowRow.pkWorkflow, workflow);
                }

            }
            catch (Exception ex)
            {
                WriteLog(ex.Message, "PrepareWorkflowList", eventLevelConstants.Error);
            }

        }

        private void StartAllWorkflows()
        {
            try
            {
                foreach (var workflow in _workflowDic.Values)
                {
                    workflow.StartAsync();
                    workflow.EventOccured += WorkflowEventOccured;
                    var workflowSubscriber = new WorkflowSubscriber(workflow);
                    _workflowSubscriberDic.Add(workflow.WorkflowRow.pkWorkflow, workflowSubscriber);
                }

                EngineStarted = true;

                TestFingerPrint();
            }
            catch (Exception ex)
            {
                WriteLog(ex.Message, "StartAllWorkflows", eventLevelConstants.Error);
            }
        }

        private void TestFingerPrint(){
            try{
                var fps = new FingerPrintEngineServerClient("TcpBinding");
                if (fps.ClientCredentials != null)
                {
                    fps.ClientCredentials.Windows.ClientCredential.Domain = Settings.Default.Domain;
                    fps.ClientCredentials.Windows.ClientCredential.UserName = Settings.Default.Username;
                    fps.ClientCredentials.Windows.ClientCredential.Password = Settings.Default.Password;
                    string error;
                    fps.Open();
                    var bitmap = Properties.Resources.SampleFingerPrintImage;
                    var bitArr = new byte[bitmap.Width * bitmap.Height];
                    for (var h = 0; h < bitmap.Height; h++)
                        for (var w = 0; w < bitmap.Width; w++)
                            bitArr[h * bitmap.Width + w] = bitmap.GetPixel(w, h).R;
                    var pkPerson = fps.IdentifyPerson(out error, bitArr);
                    WriteLog("Test Fingerprint OK " + pkPerson, "StartAllWorkflows", eventLevelConstants.Info);

                    fps.Close();
                }
                
            }
            catch (Exception ex){
                WriteLog(ex.Message + "\nUsername:"+Settings.Default.Username, "StartAllWorkflows", eventLevelConstants.Error);
            }

        }

        private void EndAllWorkflows()
        {
            try
            {
                foreach (var workflow in from AccessControlDS.tblWorkflowRow wf in WorkflowEngine.tblWorklow.Rows select new WorkflowEngine(wf))
                {
                    workflow.End();
                    _workflowSubscriberDic.Remove(workflow.WorkflowRow.pkWorkflow);
                }

                EngineStarted = false;
            }
            catch (Exception ex)
            {
                WriteLog(ex.Message, "EndAllWorkflows", eventLevelConstants.Error);
            }

        }

        #endregion

        #region Public Properties

        public Boolean EngineStarted { get; set; }

        #endregion

        #region Event Handlers

        private static void WorkflowEventOccured(AccessControlDS.tblWorkflowRow workflowRow, AccessControlDS.tblEventLogDataTable eventLogDatatable)
        {
            try
            {
                //UpdateClientList();
                var clients = _workflowSubscriberDic[workflowRow.pkWorkflow].SubscriberClientList.ToArray();
                foreach (var clientID in clients)
                {

                    try
                    {
                        _callbackDic[clientID].NotifiyClientWorkflowEventOccured(eventLogDatatable);
                    }
                    catch (Exception)
                    {
                        //UpdateClientList();
                        _workflowSubscriberDic[workflowRow.pkWorkflow].SubscriberClientList.Remove(clientID);
                    }
                }
                

            }
            catch (Exception ex)
            {
                WriteLog(ex.Message, "WorkflowEventOccured", eventLevelConstants.Error);
            }

        }

        #endregion

        #region Private Method

        private static void UpdateClientList()
        {
            try
            {
                foreach (var workflowEngineCallback in _callbackDic.Keys)
                {
                    try
                    {
                        _callbackDic[workflowEngineCallback].CheckConnectionAlive();
                    }
                    catch (Exception)
                    {
                        _callbackDic.Remove(workflowEngineCallback);
                        foreach (var workflowSubscriber in _workflowSubscriberDic)
                        {
                            if (workflowSubscriber.Value.SubscriberClientList.Contains(workflowEngineCallback))
                                workflowSubscriber.Value.SubscriberClientList.Remove(workflowEngineCallback);
                        }

                    }
                }

            }
            catch (Exception ex)
            {
                WriteLog(ex.Message, "DispoUpdateClientListse", eventLevelConstants.Error);
            }

        }

        private static void WriteLog(String message, String callerMethod, eventLevelConstants level)
        {
            LogFile.WriteLog(LogFile.DefaultLogfileLocation + "WorkflowService\\" + DateTime.Now.Date.ToString("yyyyMMdd") + ' ' + callerMethod + ".txt", level, message);
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            try
            {
                EndAllWorkflows();
            }
            catch (Exception ex)
            {
                WriteLog(ex.Message, "Dispose", eventLevelConstants.Error);
            }
        }

        #endregion

        #region IWorkflowEngineServer Members

        public void Register(Guid clientID, int[] workflowIDList)
        {
            try
            {
                var clientMessages = OperationContext.Current.GetCallbackChannel<IWorkflowEngineCallback>();
                if (!_callbackDic.ContainsKey(clientID))
                {
                    _callbackDic.Add(clientID, clientMessages);
                }

                foreach (var workflow in workflowIDList)
                {
                    if (!_workflowSubscriberDic[workflow].SubscriberClientList.Contains(clientID))
                        _workflowSubscriberDic[workflow].SubscriberClientList.Add(clientID);
                }

                foreach (var workflow in _workflowSubscriberDic.Keys)
                {
                    if (_workflowSubscriberDic[workflow].SubscriberClientList.Contains(clientID)
                        && !workflowIDList.Contains(workflow))
                        _workflowSubscriberDic[workflow].SubscriberClientList.Remove(clientID);
                }

                EventLog.WriteEntry("Workflow Service", String.Format("Client with ID : \"{0}\" Registered for workflows "+string.Join(",",workflowIDList), clientID), EventLogEntryType.Information);
            }
            catch (Exception ex)
            {

                WriteLog(ex.Message, "Register", eventLevelConstants.Error);
            }
        }

        public void LetOnePersonPass(int workflowID)
        {
            try
            {
                _workflowDic[workflowID].VaKonGateSabmordaro();
            }
            catch (Exception ex)
            {
                WriteLog(ex.Message, "LetOnePersonPass", eventLevelConstants.Error);
            }
        }

        public void UnRegister(Guid clientID)
        {
            try
            {
                if (!_callbackDic.ContainsKey(clientID))
                    return;
                _callbackDic.Remove(clientID);
                foreach (var workflowSubscriber in
                    _workflowSubscriberDic.Where(workflowSubscriber => workflowSubscriber.Value.SubscriberClientList.Contains(clientID)))
                {
                    workflowSubscriber.Value.SubscriberClientList.Remove(clientID);
                }
            }
            catch (Exception ex)
            {
                WriteLog(ex.Message, "LetOnePersonPass", eventLevelConstants.Error);
            }
        }

        public bool CheckConnection()
        {
            return true;
        }

        public void RestartService()
        {
            foreach (var process in Process.GetProcesses())
            {
                if(process.ProcessName.StartsWith("Access Control Service"))
                    process.Kill();
            }
        }

        #endregion

    }
}
